﻿using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;
using ADI.Security.Membership;

namespace ADI.Security.Authorization
{

    public class ADISSOUrlAuthorizationModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.AuthorizeRequest += new EventHandler(context_AuthorizeRequest);
        }

        void context_AuthorizeRequest(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            HttpContext context = app.Context;

            Configuration config = WebConfigurationManager.OpenWebConfiguration(null);
            var locations = ((ADISSOLocations)ConfigurationManager.GetSection("ADISSOLocations")).Locations;

            if (locations == null)
            {
                return;
            }

            //tolgo il primo backslash (/) dalla stringa
            string currentPage = context.Request.Url.AbsolutePath.Substring(1).ToLower();
            ADISSOLocation manage = null;
            foreach (ADISSOLocation l in locations)
            {
                if (this.CheckIfUrlIsInLocation(l.path.ToLower(), currentPage, context))
                //if (l.path.ToLower() == currentPage)
                {
                    //se lo trovo non è necessario continuare
                    manage = l;
                    break;
                }
            }
            if (manage != null)
            {
                if (manage.allow.ElementInformation.IsPresent)
                {
                    // in questo caso se il ruolo o i ruoli dell'utente NON combaciano mostro l'errore
                    if (this.CheckElementForItems(manage.allow, context.User.Identity.Name, context) == false)
                        this.SendNotAuthorized(app);
                }
                if (manage.deny.ElementInformation.IsPresent)
                {
                    // in questo caso se il ruolo o i ruoli dell'utente combaciano mostro l'errore
                    if (this.CheckElementForItems(manage.deny, context.User.Identity.Name, context))
                        this.SendNotAuthorized(app);
                }
            }
        }

        public bool CheckElementForItems(ConfigurationElement location, string Name, HttpContext context)
        {
            bool hasItems = false;
            string[] roles = null;
            string[] manager = null;

            if (location is AllowElement)
            {
                AllowElement allow = (AllowElement)location;
                if (!string.IsNullOrEmpty(allow.roles))
                    roles = allow.roles.Split(',').Select(r => r.Trim().ToLower()).ToArray();
                else
                    throw new ArgumentException("roles non può essere vuoto.");

                if (!string.IsNullOrEmpty(allow.requireManager))
                    manager = allow.requireManager.Split(',').Select(r => r.Trim().ToLower()).ToArray();
                else
                    manager = new string[] { string.Empty };
            }
            else if (location is DenyElement)
            {
                DenyElement allow = (DenyElement)location;
                if (!string.IsNullOrEmpty(allow.roles))
                    roles = allow.roles.Split(',').Select(r => r.Trim().ToLower()).ToArray();
                else
                    throw new ArgumentException("roles non può essere vuoto.");

                manager = new string[] { string.Empty };
            }

            foreach (var r in roles)
            {
                if (r == "*")
                {
                    if (context.User != null && context.User.Identity.IsAuthenticated)
                        return true;
                    else
                        return false;
                }
                if (r == "?")
                {
                    if (context.User == null && context.User.Identity.IsAuthenticated == false)
                        return true;
                    else
                        return false;
                }

                bool isInRole = Roles.IsUserInRole(r);
                bool requiresManager = manager.Contains(r);
                bool isManager = false;
                if (requiresManager)
                    isManager = ((ADISSORoleProvider)(Roles.Provider)).IsUserManager(Name, r);

                if (isInRole)
                {
                    if (requiresManager && isManager == false) continue;
                    hasItems = true;
                    break;
                }
            }
            return hasItems;
        }

        private void SendNotAuthorized(HttpApplication context)
        {
            FormsAuthentication.RedirectToLoginPage();
        }

        private bool CheckIfUrlIsInLocation(string locationPath, string urlToCheck, HttpContext context)
        {
            bool returnValue = false;
            FileAttributes fromLocation = File.GetAttributes(context.Server.MapPath(string.Format("~/{0}", locationPath)));
            FileInfo fromUrl = new FileInfo(context.Server.MapPath(string.Format("~/{0}", urlToCheck)));

            if ((fromLocation & FileAttributes.Directory) != 0) //se il path del location è una cartella
            {
                DirectoryInfo di = new DirectoryInfo(context.Server.MapPath(string.Format("~/{0}", locationPath)));
                var lol = di.GetFiles(fromUrl.Name, SearchOption.AllDirectories).Select(g => g.FullName.ToLower()).ToList();
                returnValue = lol.Contains(fromUrl.FullName.ToLower());
            }
            else //se il path della location è un file
            {
                FileInfo location = new FileInfo(context.Server.MapPath(string.Format("~/{0}", locationPath)));
                returnValue = location.FullName.Equals(fromUrl.FullName);
            }

            return returnValue;
        }

        public void Dispose()
        { }
    }
}